using System;
using NUnit.Framework;
using ValidationFramework.Reflection;

namespace ValidationFramework.Tests
{
    public class ComparePropertyRuleTest
    {

        const string expectedPropertyToCompare = "property1";
        const string expectedErrorMessage = "expectedErrorMessage";
        const string expectedRuleSet = "EXPECTEDRULESET";
        const CompareOperator expectedCompareOperator = CompareOperator.GreaterThan;
 
        
        [TestFixture]
        public class Construction
        {

            [Test]
            public void Simple1()
            {
                var rule = new ComparePropertyRule(expectedPropertyToCompare, expectedCompareOperator);
                Assert.AreEqual(expectedPropertyToCompare, rule.PropertyToCompare);
                Assert.AreEqual(expectedCompareOperator, rule.CompareOperator);
                Assert.IsFalse(rule.UseErrorMessageProvider);
            }


            [Test]
            [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: propertyToCompare")]
            public void ExceptionPropertyToCompareNull1()
            {
                new ComparePropertyRule(null, expectedCompareOperator);
            }


            [Test]
            [ExpectedException(typeof(ArgumentException), ExpectedMessage = "String cannot be empty.\r\nParameter name: propertyToCompare")]
            public void ExceptionPropertyToCompareEmpty1()
            {
                new ComparePropertyRule(string.Empty, expectedCompareOperator);
            }


            [Test]
            public void Simple2()
            {
                var rule = new ComparePropertyRule(expectedPropertyToCompare, expectedCompareOperator);
                Assert.AreEqual(expectedPropertyToCompare, rule.PropertyToCompare);
                Assert.AreEqual(expectedCompareOperator, rule.CompareOperator);
            }


            [Test]
            [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: propertyToCompare")]
            public void ExceptionPropertyToCompareNull2()
            {
                new ComparePropertyRule(null, expectedCompareOperator);
            }


            [Test]
            [ExpectedException(typeof(ArgumentException), ExpectedMessage = "String cannot be empty.\r\nParameter name: propertyToCompare")]
            public void ExceptionPropertyToCompareEmpty2()
            {
                new ComparePropertyRule(string.Empty, expectedCompareOperator);
            }


        
            [Test]
            public void Simple3()
            {
                var rule = new ComparePropertyRule(expectedPropertyToCompare, expectedCompareOperator);
                Assert.AreEqual(expectedPropertyToCompare, rule.PropertyToCompare);
                Assert.AreEqual(expectedCompareOperator, rule.CompareOperator);
            }


            [Test]
            [ExpectedException(typeof(ArgumentException), ExpectedMessage = "String cannot be empty.\r\nParameter name: propertyToCompare")]
            public void ExceptionPropertyToCompareEmpty3()
            {
                new ComparePropertyRule(string.Empty, expectedCompareOperator);
            }


            [Test]
            [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: propertyToCompare")]
            public void ExceptionPropertyToCompareNull3()
            {
                new ComparePropertyRule(null, expectedCompareOperator);
            }

        }


        [TestFixture]
        public class IsEquivalent
        {

            [Test]
            public void Simple()
            {
                const string expectedPropertyToCompare2 = "property2";
                var rule1 = new ComparePropertyRule(expectedPropertyToCompare, CompareOperator.LessThan);
                var rule2 = new ComparePropertyRule(expectedPropertyToCompare, CompareOperator.LessThan);
                Assert.IsTrue(rule1.IsEquivalent(rule2));
                rule2 = new ComparePropertyRule(expectedPropertyToCompare, CompareOperator.GreaterThan);

                Assert.IsFalse(rule1.IsEquivalent(rule2));
                rule2 = new ComparePropertyRule(expectedPropertyToCompare2, CompareOperator.LessThan);

                Assert.IsFalse(rule1.IsEquivalent(rule2));

            }
        }


        [TestFixture]
        public class Validate
        {

            [Test]
            [ExpectedException(typeof(InvalidOperationException))]
            public void NotFindProperty()
            {

                var descriptor = TypeCache.GetType(typeof(TestClass).TypeHandle);
                var rule = descriptor.Properties["Property1"].Rules[0];
                var testClass = new TestClass { Property1 = null };
                Assert.IsFalse(rule.Validate(testClass, 0, null, null));

            }


            [Test]
            public void Simple()
            {

                var descriptor = TypeCache.GetType(typeof(TestClass).TypeHandle);
                var rule = descriptor.Properties["Property2"].Rules[0];
                var testClass = new TestClass();


                Assert.IsTrue(rule.Validate(testClass, null, null, null));
                Assert.IsTrue(rule.Validate(testClass, "b", null, null));

                testClass.Property1 = "b";
                Assert.IsFalse(rule.Validate(testClass, "a", null, null));
                Assert.IsFalse(rule.Validate(testClass, "b", null, null));
                Assert.IsTrue(rule.Validate(testClass, "c", null, null));

            }
        }


        [TestFixture]
        public class RuleInterpretation
        {
            [Test]
            public void Simple()
            {
                var rule = new ComparePropertyRule(expectedPropertyToCompare, CompareOperator.GreaterThan);
                Assert.IsFalse(string.IsNullOrEmpty(rule.RuleInterpretation));
            }
        }


        [TestFixture]
        public class CheckTypes
        {
            [Test]
            [ExpectedException(typeof(InvalidOperationException))]
            public void ExceptionInvalidProperty()
            {
                var rule = new ComparePropertyRule("Property3", CompareOperator.GreaterThan);
                rule.CheckType(new MockParameterDescriptor<string>("foo"));
            }

        }
    }

    class TestClass
    {
        [ComparePropertyRule("Property1", CompareOperator.GreaterThan)]
        public string Property2 { get; set; }


        [ComparePropertyRule("property3", CompareOperator.GreaterThan)]
        public string Property1 { get; set; }
    }
}